#include "Courbe.h"
#include <string>
#include <fstream>
#include "FonctionTemplate.h"
#include "Ecran.h"


using namespace std;

//Constructeur par defaut : courbe de degré 3 avec vecteurs b0,...,b3 nuls.
Courbe::Courbe()
{
    _degre = 3;
    _vecteur = new Vecteur[_degre +1];
    for(int i = 0 ; i <= _degre; i++)
        _vecteur[i] = Vecteur(0, 0);
}

//Constructeur à nombre de paramètres variable
Courbe::Courbe(int d, Vecteur * v)
{
    _degre = d;
    _vecteur = v;
}

//Constructeur à partir d'un fichier
Courbe::Courbe(string chemin)
{
    std::ifstream fichier(chemin.c_str(), ios::in);
    double x,y;

    if(fichier)
    {
        fichier >> _degre;

        _vecteur = new Vecteur[_degre +1];

        for(int i = 0;i <= _degre; i++)
        {
            fichier >> x;
            fichier >> y;
            _vecteur[i] = Vecteur(x, y);

            //std::cout << "i=" << i << ":" << (string)_vecteur[i] << std::endl;
        }
        fichier.close();
    }
    else
    {
        cerr << "Erreur à l'ouverture !" << endl;
        _degre = -1;
    }
}

//Renvoi le degre de la courbe
int Courbe::getDegre() const
{
    return _degre;
}

//Renvoi l'ensemble des n vecteurs de la courbe
Vecteur * Courbe::getVecteur() const
{
    return _vecteur;
}

//Algo de Casteljau : recupère un point de la courbe
Point Courbe::calculPoint(double t) const
{
    Vecteur Casteljau[getDegre()+1];
    int k = getDegre();

    for(int i = 0 ; i <= getDegre() ; i++)
      Casteljau[i] = Vecteur(getVecteur()[i]);

    boucleCasteljau(Casteljau,k,t);

    return Point(Casteljau[0].x,Casteljau[0].y);
}

void Courbe::boucleCasteljau(Vecteur * tab, int d, double t)const
{
    while(d >= 0)
    {
        for (int i = 0; i < d; i++)
        {
            tab[i] = ((tab[i] * (1 - t)) + (tab[i + 1] * t));
        }
        d--;
    }
}

Point * Courbe::getEnsPt(int n) const
{
	Point * tabP = new Point[n+1];
	double pas = 1.0 /n;
	double t = 0.0;

	for(int i = 0; i <= n; i++)
	{
	    tabP[i] = calculPoint(t);
	    t = t + pas;
	}

	return tabP;
}

void Courbe::elevation(Vecteur * tab)const
{
    double alpha;

    tab[0] = _vecteur[0];
    tab[_degre + 1] = _vecteur[_degre];

    for ( int i = 1 ; i < (_degre + 1) ; i++ )
    {
        alpha = ((double)i / (double)(_degre + 1));
        tab[i] = ( alpha*_vecteur[i-1] ) + ( (1.0 - alpha)*_vecteur[i] );
    }
}

Courbe Courbe::operator++(int useless) const
{
    int nbV = _degre + 2;
    Vecteur * tabV = new Vecteur[nbV];

    elevation(tabV);

    return Courbe( nbV - 1 , tabV );
}

Courbe Courbe::operator--(int useless) const
{
    Vecteur * tabV = new Vecteur[_degre];
    diminution(tabV);
    return Courbe( _degre - 1 , tabV );
}

void Courbe::diminution(Vecteur * tab)const
{
    //extern Ecran * e;
    double alpha, alphaSup;

    Vecteur * Pi = new Vecteur[_degre];
    Vecteur * Qi = new Vecteur[_degre];

    cout << "0=" << (string)_vecteur[0] << " fin=" << (string)_vecteur[_degre] << endl;;

    tab[0] = _vecteur[0];
    tab[_degre - 1 ] = _vecteur[_degre];

    Pi[0] = _vecteur[0];
    //Pi[_degre - 1 ] = _vecteur[_degre];

    //Qi[0] = _vecteur[0];
    Qi[_degre - 1 ] = _vecteur[_degre];

    double Bi;

    cout << "PI----------------------------" << endl;
    for ( int i = 1 ; i < _degre; i++ )
    {
        alpha = ((double)(i) / (double)(_degre + 1.0));
        Vecteur etape1 = -(alpha * Pi[i-1]);
        Vecteur etape2 = _vecteur[i] + etape1;
        double etape3 = (double)(1.0 / ( 1.0 - alpha));
        Vecteur etape4 = etape3 * etape2;

        Pi[i] = etape4;

        cout << "alpha=" << alpha << " _vecteur[i]=" << (string)_vecteur[i] << " i=" << i << endl;
        cout << "Etape 1 " << (string)etape1 << endl;
        cout << "Etape 2 " << (string)etape2 << endl;
        cout << "Etape 3 " << etape3 << endl;
        cout << "Etape 4 " << (string)etape4 << endl;
    }

    cout << "QI----------------------------" << endl;

    for(int j = _degre - 2; j >= 0; j--)
    {
        alphaSup = (double)(j + 1) / (double)(_degre + 1.0);
        Vecteur etape1 = -((1.0 - alphaSup) * Qi[j+1]);
        Vecteur etape2 = _vecteur[j+1] + etape1;
        double etape3 = (double)(1.0 / alphaSup);
        Vecteur etape4 = etape3 * etape2;

        Qi[j] = etape4;

        cout << "alpha=" << alphaSup << " _vecteur[j+1]=" << (string)_vecteur[j+1] << " j=" << j << endl;
        cout << "Etape 1 " << (string)etape1 << endl;
        cout << "Etape 2 " << (string)etape2 << endl;
        cout << "Etape 3 " << etape3 << endl;
        cout << "Etape 4 " << (string)etape4 << endl;
    }

    for(int k = 1; k < (_degre - 1); k++)
    {
        Bi = ((double)(k)) / (double)(_degre - 1);
        tab[k] = ((1 - Bi) * Pi[k]) + (Bi * Qi[k]);
    }


    Courbe cpi(_degre -1 ,Pi);
    Courbe cqi(_degre -1 ,Qi);


    //e->drawCourbe(cpi,100,e->getCouleur(0,255,0));
    //e->drawCourbe(cqi,100,e->getCouleur(0,0,255));


    delete[] Pi;
    delete[] Qi;
}

/*
    extern Ecran * e;
    double alpha, alphaSup;

    Vecteur * Pi = new Vecteur[_degre];
    Vecteur * Qi = new Vecteur[_degre];

    tab[0] = _vecteur[0];
    tab[_degre - 1 ] = _vecteur[_degre];

    Pi[0] = _vecteur[0];
    Pi[_degre - 1 ] = _vecteur[_degre];

    Qi[0] = _vecteur[0];
    Qi[_degre - 1 ] = _vecteur[_degre];

    double Bi;

    for ( int i = 1 ; i < _degre - 1 ; i++ )
    {
        //CALCUL DU PI
        alpha = ((double)(i) / (double)(_degre));
        Pi[i] = (1.0 / (1.0 - alpha)) * (_vecteur[i] - (alpha * Pi[i-1]));
    }

    for(int j = _degre - 2; j > 0; j--)
    {
        //CALCUL DU QI
        alphaSup = (double)(j+1) / (double)(_degre);
        Qi[j] = ( (double)1.0 / (double)alphaSup) * ( _vecteur[j + 1] - ( (1.0 - alphaSup) * Qi[j+1]));
    }

    for(int k = 1; k < (_degre - 1); k++)
    {
        //CALCUL DU BI
        Bi = ((double)(k)) / (double)(_degre - 1);

        //CALCUL DU AI
        tab[k] = ((1 - Bi) * Pi[k]) + (Bi * Qi[k]);
        //tab[k] = Qi[k];
    }
    Courbe cpi(_degre -1 ,Pi);
    Courbe cqi(_degre -1 ,Qi);

    e->drawCourbe(cpi,100,e->getCouleur(0,255,0));
    e->drawCourbe(cqi,100,e->getCouleur(0,0,255));

    delete[] Pi;
    delete[] Qi;

*/

/*
    double alpha, alphaSup;

    Vecteur * Pi = new Vecteur[_degre];
    Vecteur * Qi = new Vecteur[_degre];

    tab[0] = _vecteur[0];
    tab[_degre - 1 ] = _vecteur[_degre];

    Pi[0] = _vecteur[0];
    Pi[_degre - 1 ] = _vecteur[_degre];

    Qi[0] = _vecteur[0];
    Qi[_degre - 1 ] = _vecteur[_degre];

    double Bi;

    for ( int i = 1 ; i < _degre - 1 ; i++ )
    {
        //CALCUL DU PI
        alpha = ((double)i / (double)(_degre - 1));
        Pi[i] = (1.0 / (1.0 - alpha)) * (_vecteur[i] - (alpha * Pi[i-1]));
    }

    for(int j = _degre - 2; j > 0; j--)
    {
        //CALCUL DU QI
        alphaSup = (double)(j) / (double)(_degre - 1);
        Qi[j] = (1.0 / alphaSup) * (_vecteur[j+1] - ((1.0 -alphaSup) * Qi[j+1]));
    }

    for(int k = 1; k < (_degre - 1); k++)
    {
        //CALCUL DU BI
        Bi = ((double)(k)) / (double)(_degre - 1);

        //CALCUL DU AI
        tab[k] = ((1 - Bi) * Pi[k]) + (Bi * Qi[k]);
        //tab[k] = Qi[k];
    }
*/



Courbe Courbe::derivee() const
{
    Vecteur * tabV = new Vecteur[getDegre()];

    for(int i = 0 ; i < getDegre() ; i++)
      tabV[i] = ((double)getDegre())*(_vecteur[i+1] - _vecteur[i]);

    return Courbe(getDegre()-1,tabV);
}

Point * Courbe::getPtEnglobant()
{
    Point * tabP = getEnsPt(200);

    int xMin = tabP[0].x , xMax  = tabP[0].x , yMin = tabP[0].y , yMax = tabP[0].y ;

    for ( int i = 0 ; i < 200 ; i++)
    {
        Point p = tabP[i];

        if( p.x < xMin)
            xMin = p.x;

        if ( p.x > xMax )
            xMax = p.x;

        if( p.y < yMin)
            yMin = p.y;

        if ( p.y > yMax )
            yMax = p.y;
    }

    Point hg(xMin,yMax);
    Point hd(xMax,yMax);
    Point bg(xMin,yMin);
    Point bd(xMax,yMin);

    Point * tab = new Point[4];
    tab[0] = hg;
    tab[1] = hd;
    tab[2] = bd;
    tab[3] = bg;

    return tab;
}

void Courbe::translater(Vecteur v)
{
    for(int i = 0 ; i < (_degre + 1) ; i++)
    {
        _vecteur[i].x += v.x;
        _vecteur[i].y += v.y;
    }
}

void Courbe::normaliser()
{
    Point* tab = getPtEnglobant();

    Vecteur v( - tab[3].x , - tab[3].y  );
    translater(v);

    tab = getPtEnglobant();

    double ratioLargeur;

    if(tab[1].x > 250.0)
        ratioLargeur = tab[1].x / 250.0;
    else if(tab[1]. x <= 250.0)
        ratioLargeur = 250.0 / tab[1].x;

    double ratioHauteur;
    if(tab[1].y > 250.0)
        ratioHauteur = tab[1].y / 250.0;
    else if(tab[1].y <= 250.0)
        ratioHauteur = 250.0 / tab[1].y;

    for(int i = 0 ; i < (_degre + 1) ; i++)
    {
        _vecteur[i].x = _vecteur[i].x * ratioLargeur;
        _vecteur[i].y = _vecteur[i].y * ratioHauteur;
    }


}

void Courbe::affiche()const
{
    for(int i = 0; i <= _degre; i ++)
        std::cout << (string)_vecteur[i] << std::endl;
}

void Courbe::sauvegarde(string chemin)
{
    string commande = "MD Alphabet\\";
    commande += chemin;
    system(commande.c_str());

    string pathFichier = "Alphabet\\";
    pathFichier += chemin;
    pathFichier += "/";
    pathFichier += chemin;
    pathFichier += ".txt";

    ofstream fichier(pathFichier.c_str(), ios::out | ios::trunc);  // ouverture en écriture avec effacement du fichier ouvert

    if(fichier)
    {
            fichier << _degre << endl;

            for (int i = 0 ; i <= _degre ; i++ )
                fichier << (string)_vecteur[i] << endl;

            fichier.close();
    }
    else
            cerr << "Impossible d'ouvrir le fichier !" << endl;
}
